/** HTTP 方法 */
export type Method = "GET" | "POST" | "PUT" | "DELETE";
/**
 * 为方法调用定义一个统一的配置接口
 */
export interface RequestOptions {
  /**
   * URL 查询参数 (主要用于 GET 请求)
   */
  params?: Record<string, any>;
  /**
   * 请求体 (用于 POST, PUT, DELETE 请求)
   */
  data?: any;
  /**
   * 自定义请求头
   */
  headers?: Record<string, string>;
  /**
   * 进度回调
   * @param progress - 进度百分比 (0-100)
   */
  onProgress?: (progress: number) => void;
  /**
   * 请求超时时间 (单位: 毫秒)
   */
  timeout?: number;
  /**
   * 用于取消请求的 AbortSignal
   */
  signal?: AbortSignal;
  /** 响应方式 */
  responseType?: "arraybuffer" | "blob" | "document" | "json" | "text" | "stream";
}
/** 请求配置，不包含params和data */
export type RequestConfig = Omit<RequestOptions, "params" | "data">;

/**
 * 封装 XMLHttpRequest 的网络请求类
 */
export default class Request {
  private static readonly baseURL: string = import.meta.env.VITE_API_URL || "";
  private static decoratorCache = new Map<string, Function>();
  /**
   * 类型守卫，用于判断一个对象是否是 RequestConfig 类型
   * @param obj - 需要检查的对象
   * @returns boolean
   */
  private static isRequestConfig(obj: any): obj is RequestConfig {
    if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
      return false;
    }
    // RequestConfig 的属性键
    const configKeys: (keyof RequestConfig)[] = ["headers", "onProgress", "timeout", "signal"];
    // 检查对象是否至少包含一个 RequestConfig 的键
    return configKeys.some((key) => key in obj);
  }

  public static ajax<T>(method: Method, url: string, options: RequestOptions = {}): Promise<T> {
    return new Promise((resolve, reject) => {
      const { params, data, headers, onProgress, timeout = 1000 * 60 * 5, signal } = options;
      const xhr = new XMLHttpRequest();
      // 如果请求已被取消，则直接拒绝
      if (signal?.aborted) {
        return reject(new DOMException("Request aborted", "AbortError"));
      }
      let fullURL: string;
      // 检查 url 是否为绝对路径
      if (/^(?:[a-z]+:)?\/\//i.test(url)) {
        fullURL = url;
      } else {
        // 改进 URL 拼接，避免双斜杠或缺少斜杠的问题
        fullURL =
          (this.baseURL.endsWith("/") ? this.baseURL.slice(0, -1) : this.baseURL) +
          (url.startsWith("/") ? url : "/" + url);
      }
      // 处理 URL 查询参数 (params)
      if (params) {
        const queryString = Object.keys(params)
          .map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
          .join("&");
        if (queryString) {
          fullURL += (fullURL.includes("?") ? "&" : "?") + queryString;
        }
      }
      xhr.open(method, fullURL, true);
      // 设置超时时间
      if (timeout) {
        xhr.timeout = timeout;
      }
      // 设置请求头
      if (headers) {
        for (const key in headers) {
          if (Object.prototype.hasOwnProperty.call(headers, key)) {
            xhr.setRequestHeader(key, headers[key]);
          }
        }
      }
      // 如果不是 FormData，并且是 POST/PUT/DELETE，且用户未提供 Content-Type，则设置默认值
      const hasCustomContentType =
        headers &&
        (Object.prototype.hasOwnProperty.call(headers, "Content-Type") ||
          Object.prototype.hasOwnProperty.call(headers, "content-type"));

      if (
        (method === "POST" || method === "PUT" || method === "DELETE") &&
        data &&
        !(data instanceof FormData) &&
        !hasCustomContentType
      ) {
        xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
      }

      xhr.onload = () => {
        if (xhr.status >= 200 && xhr.status < 300) {
          try {
            // 空响应体是有效的，直接 resolve
            if (xhr.responseText === "") {
              resolve("" as any);
              return;
            }
            // 优先尝试解析 JSON，如果失败则直接返回文本
            resolve(JSON.parse(xhr.responseText));
          } catch (e) {
            resolve(xhr.responseText as any);
          }
        } else {
          // 提供更详细的错误信息
          reject({
            status: xhr.status,
            statusText: xhr.statusText,
            response: xhr.responseText,
          });
        }
      };

      // 监听请求错误
      xhr.onerror = () => {
        reject(new Error("Network Error"));
      };

      // 监听超时
      xhr.ontimeout = () => {
        reject(new Error(`Request timed out after ${timeout} ms`));
      };

      // 监听取消信号
      const abortHandler = () => {
        xhr.abort();
        reject(new DOMException("Request aborted", "AbortError"));
      };

      if (signal) {
        signal.addEventListener("abort", abortHandler);
      }

      // 清理逻辑，在请求完成、失败或中止时移除监听器
      const cleanup = () => {
        if (signal) {
          signal.removeEventListener("abort", abortHandler);
        }
      };
      xhr.addEventListener("loadend", cleanup);

      // 监听进度 (包括上传和下载)
      if (onProgress) {
        // 上传进度
        if (xhr.upload) {
          xhr.upload.onprogress = (event: ProgressEvent) => {
            if (event.lengthComputable) {
              const progress = Math.floor((event.loaded / event.total) * 100);
              onProgress(progress);
            }
          };
        }
        // 下载进度
        xhr.onprogress = (event: ProgressEvent) => {
          if (event.lengthComputable) {
            const progress = Math.floor((event.loaded / event.total) * 100);
            onProgress(progress);
          }
        };
      }

      // 发送请求
      if (method === "GET") {
        xhr.send();
      } else {
        if (data instanceof FormData) {
          xhr.send(data);
        } else if (data !== undefined && data !== null) {
          xhr.send(JSON.stringify(data));
        } else {
          xhr.send();
        }
      }
    });
  }

  /**
   * 创建一个通用的HTTP方法装饰器工厂
   * @param method - HTTP 方法 ('GET', 'POST', 'PUT', 'DELETE')
   * @private
   */
  private static createDecorator(method: Method) {
    return (url: string) => {
      const cacheKey = `${method}:${url}`;
      if (this.decoratorCache.has(cacheKey)) {
        return this.decoratorCache.get(cacheKey)!;
      }
      const decorator = (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
        const originalMethod = descriptor.value; // 保存原始方法
        descriptor.value = async function (...args: any[]) {
          let payload: any;
          let callTimeOptions: RequestConfig = {};
          if (args.length === 1) {
            const arg0 = args[0];
            // 使用类型守卫来判断参数类型
            if (Request.isRequestConfig(arg0)) {
              callTimeOptions = arg0;
            } else {
              payload = arg0;
            }
          } else if (args.length >= 2) {
            // 规则: 第一个参数是 payload, 第二个参数是 callTimeOptions
            payload = args[0];
            callTimeOptions = args[1] || {};
          }
          const finalOptions: RequestOptions = {
            ...callTimeOptions,
          };
          if (method === "GET" || method === "DELETE") {
            finalOptions.params = { ...payload };
          } else {
            finalOptions.data = { ...payload };
          }
          const response = await Request.ajax<any>(method, url, finalOptions);
          return await originalMethod.apply(this, [...args, response]);
        };
      };
      this.decoratorCache.set(cacheKey, decorator);
      return decorator;
    };
  }
  public static get = Request.createDecorator("GET");
  public static post = Request.createDecorator("POST");
  public static put = Request.createDecorator("PUT");
  public static delete = Request.createDecorator("DELETE");
}
